﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using System.Windows.Input;
using System.Windows;
using Phydeaux.Utilities;

namespace Bixas.Tangibilis.Input
{
    public class InputElementWrapper
    {

        private static Phydeaux.Utilities.Func<Point, Point, DependencyObject, DependencyObject> TranslatePointWrapper;
        private static Phydeaux.Utilities.Func<DependencyObject, DependencyObject> GetContainingVisualWrapper;
        private static Phydeaux.Utilities.Func<IInputElement, bool> IsValidWrapper;
        private static Phydeaux.Utilities.Func<DependencyObject, bool> IsUIElementWrapper;

        static MethodInfo translatePointMethod;

        static InputElementWrapper()
        {
            InitWrappers();
            Type inputElementType = typeof(InputManager).Assembly.GetType("System.Windows.Input.InputElement");
            translatePointMethod = inputElementType.GetMethod("TranslatePoint", BindingFlags.NonPublic | BindingFlags.Static, null,
                new[] { typeof(Point), typeof(DependencyObject), typeof(DependencyObject) }, null);
		
        }

        public static Point TranslatePoint(Point point, DependencyObject from, DependencyObject to)
        {
            //return TranslatePointWrapper(point, from, to);
            Point retPoint = (Point)translatePointMethod.Invoke(null, new object[] { point, from, to });
            return retPoint;
        }

        //internal static Point TranslatePoint(Point pt, DependencyObject from, DependencyObject to)
        //{
        //    return TranslatePointWrapper(pt, from, to);
        //}

        internal static DependencyObject GetContainingVisual(DependencyObject o)
        {
            return GetContainingVisualWrapper(o);
        }

        private static void InitWrappers()
        {
            #region Init ExecuteCommandSource method wrapper

            //retrieve internal type MS.Internal.Commands.CommandHelpers
            Type commandHelpersType = System.Reflection.Assembly.GetAssembly(typeof(InputEventArgs))
                                                            .GetType("System.Windows.Input.InputElement");

            InitTranslatePointWrapper(commandHelpersType);
            InitGetContainingVisualWrapper(commandHelpersType);
            InitIsValidWrapper(commandHelpersType);
            InitIsUIElementWrapper(commandHelpersType);
            #endregion
        }

        private static void InitGetContainingVisualWrapper(Type commandHelpersType)
        {
            //retrieve internal procedure ExecuteCommandSource(ICommandSource commandSource)
            MethodInfo GetContainingVisualMethod = commandHelpersType.GetMethod("GetContainingVisual", System.Reflection.BindingFlags.NonPublic | BindingFlags.Static);

            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm = new DynamicMethod(GetContainingVisualMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(DependencyObject) ,
                                                 new Type[] { typeof(DependencyObject) },
                                                 commandHelpersType,
                                                 true);
            dm.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de = new DynamicEmit(dm);
            de.LoadArgument(0);
            de.Call(GetContainingVisualMethod);
            //TODO: verify if cast is needed when return type is void
            de.CastTo(GetContainingVisualMethod.ReturnType, typeof(DependencyObject));
            de.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            GetContainingVisualWrapper = (Phydeaux.Utilities.Func<DependencyObject, DependencyObject>)dm.CreateDelegate(typeof(Phydeaux.Utilities.Func<DependencyObject, DependencyObject>));

        }

        private static void InitTranslatePointWrapper(Type commandHelpersType)
        {
            //retrieve internal procedure ExecuteCommandSource(ICommandSource commandSource)
            MethodInfo TranslatePointMethod = commandHelpersType.GetMethod("TranslatePoint", System.Reflection.BindingFlags.NonPublic | BindingFlags.Static ,null,new Type[] { typeof(Point), typeof(DependencyObject), typeof(DependencyObject) },null);


            
            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm = new DynamicMethod(TranslatePointMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(Point),
                                                 new Type[] { typeof(Point), typeof(DependencyObject), typeof(DependencyObject) },
                                                 commandHelpersType,
                                                 true);
            dm.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de = new DynamicEmit(dm);
            de.LoadArgument(0);
            de.LoadArgument(1);
            de.LoadArgument(2);
            de.Call(TranslatePointMethod);
            //TODO: verify if cast is needed when return type is void
            de.CastTo(TranslatePointMethod.ReturnType, typeof(Point));
            de.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            TranslatePointWrapper = (Phydeaux.Utilities.Func<Point, Point, DependencyObject, DependencyObject>)dm.CreateDelegate(typeof(Phydeaux.Utilities.Func<Point, Point, DependencyObject, DependencyObject>));

        }

        private static void InitIsValidWrapper(Type commandHelpersType)
        {
            //retrieve internal procedure ExecuteCommandSource(ICommandSource commandSource)
            MethodInfo IsValidMethod = commandHelpersType.GetMethod("IsValid", System.Reflection.BindingFlags.NonPublic | BindingFlags.Static ,null, new Type[] { typeof(IInputElement) }, null);
            
            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm = new DynamicMethod(IsValidMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(bool),
                                                 new Type[] { typeof(IInputElement)},
                                                 commandHelpersType,
                                                 true);
            dm.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de = new DynamicEmit(dm);
            de.LoadArgument(0);
            de.Call(IsValidMethod);
            //TODO: verify if cast is needed when return type is void
            de.CastTo(IsValidMethod.ReturnType, typeof(bool));
            de.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            IsValidWrapper = (Phydeaux.Utilities.Func<IInputElement, bool>)dm.CreateDelegate(typeof(Phydeaux.Utilities.Func<IInputElement, bool>));

        }

        private static void InitIsUIElementWrapper(Type commandHelpersType)
        {
            //retrieve internal procedure ExecuteCommandSource(ICommandSource commandSource)
            MethodInfo IsUIElementMethod = commandHelpersType.GetMethod("IsUIElement", System.Reflection.BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(DependencyObject) }, null);

            //create a new DynamicMethod to create a wrapper to call the required method
            DynamicMethod dm = new DynamicMethod(IsUIElementMethod.Name,
                                                 MethodAttributes.Static | MethodAttributes.Public, // public static
                                                 CallingConventions.Standard,
                                                 typeof(bool),
                                                 new Type[] { typeof(DependencyObject) },
                                                 commandHelpersType,
                                                 true);
            dm.InitLocals = false;

            //use Phydeaux codeEmit wrapper for clener code
            DynamicEmit de = new DynamicEmit(dm);
            de.LoadArgument(0);
            de.Call(IsUIElementMethod);
            //TODO: verify if cast is needed when return type is void
            de.CastTo(IsUIElementMethod.ReturnType, typeof(bool));
            de.Return();
            //Retrieve a typed delegate targeting the created wrapper method
            IsUIElementWrapper = (Phydeaux.Utilities.Func<DependencyObject, bool>)dm.CreateDelegate(typeof(Phydeaux.Utilities.Func<DependencyObject, bool>));

        }

        internal static bool IsValid(IInputElement element)
        {
            return IsValidWrapper(element);
        }

        internal static bool IsUIElement(DependencyObject o)
        {
            return IsUIElementWrapper(o);
        }

        internal static bool IsContentElement(DependencyObject o)
        {
            throw new NotImplementedException();
        }

        internal static bool IsUIElement3D(DependencyObject o)
        {
            throw new NotImplementedException();
        }
    }
}
